﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using SkeletalAnimation3D;
using System.Windows.Media.Media3D;
using System.Windows.Data;
using System.Windows.Controls;
using System.Windows.Media;
using System.IO;
using Animator3D;
using SkeletalAnimation3D.Core;
using System.ComponentModel;
namespace Animator3D.ObjectModel
{
    public class Document : DependencyObject, INotifyPropertyChanged
    {
        private MaterialLibrary _materialLibrary;
        private BoneAnimationLibrary _animationLibrary;
        private MeshInterpolator _interpolator;


        public BoneAnimation CurrentAnimation
        {
            get { return (BoneAnimation)GetValue(CurrentAnimationProperty); }
            set { SetValue(CurrentAnimationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurrentAnimation.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentAnimationProperty =
            DependencyProperty.Register("CurrentAnimation", typeof(BoneAnimation), typeof(Document), new UIPropertyMetadata(null, CurrentAnimationChanged));

        private static void CurrentAnimationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ((e.OldValue == null && e.NewValue !=null) || !e.OldValue.Equals(e.NewValue))
            {
                Document doc = d as Document;
                if (doc != null)
                {
                    doc.CurrentAnimationKeyframe = 0;
                    doc.CurrentAnimationOffset = 0.0;
                }
            }
        }

        public double CurrentAnimationOffset
        {
            get { return (double)GetValue(CurrentAnimationOffsetProperty); }
            set { SetValue(CurrentAnimationOffsetProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurrentAnimationOffset.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentAnimationOffsetProperty =
            DependencyProperty.Register("CurrentAnimationOffset", typeof(double), typeof(Document), new UIPropertyMetadata(0.0));

        public int CurrentAnimationKeyframe
        {
            get { return (int)GetValue(CurrentAnimationKeyframeProperty); }
            set { SetValue(CurrentAnimationKeyframeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurrentAnimationKeyframe.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentAnimationKeyframeProperty =
            DependencyProperty.Register("CurrentAnimationKeyframe", typeof(int), typeof(Document), new UIPropertyMetadata(0));



        public int SelectedBoneId
        {
            get { return (int)GetValue(SelectedBoneIdProperty); }
            set { SetValue(SelectedBoneIdProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedBones.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedBoneIdProperty =
            DependencyProperty.Register("SelectedBoneId", typeof(int), typeof(Document), new UIPropertyMetadata(-1));


        public bool ShowAttachedPoints
        {
            get { return (bool)GetValue(ShowAttachedPointsProperty); }
            set { SetValue(ShowAttachedPointsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ShowAttachedPoints.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ShowAttachedPointsProperty =
            DependencyProperty.Register("ShowAttachedPoints", typeof(bool), typeof(Document), new UIPropertyMetadata(false));


        public Int32Collection SelectedPointIds
        {
            get { return (Int32Collection)GetValue(SelectedPointIdsProperty); }
            set { SetValue(SelectedPointIdsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedPointIds.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedPointIdsProperty =
            DependencyProperty.Register("SelectedPointIds", typeof(Int32Collection), typeof(Document), new UIPropertyMetadata(null));




        public Point3DCollection AnimatedMeshPoints
        {
            get { return (Point3DCollection)GetValue(AnimatedMeshPointsProperty); }
            set { SetValue(AnimatedMeshPointsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AnimatedMeshPoints.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AnimatedMeshPointsProperty =
            DependencyProperty.Register("AnimatedMeshPoints", typeof(Point3DCollection), typeof(Document), new UIPropertyMetadata(null));
                

        public Vector3DCollection AnimatedNormals
        {
            get { return (Vector3DCollection)GetValue(AnimatedNormalsProperty); }
            set { SetValue(AnimatedNormalsProperty, value); }
        }



        public SkeletonInfo AnimatedSkeleton
        {
            get { return (SkeletonInfo)GetValue(AnimatedSkeletonProperty); }
            set { SetValue(AnimatedSkeletonProperty, value); }
        }

        // Using a DependencyProperty as the backing store for AnimatedSkeleton.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AnimatedSkeletonProperty =
            DependencyProperty.Register("AnimatedSkeleton", typeof(SkeletonInfo), typeof(Document), new UIPropertyMetadata(null));


        // Using a DependencyProperty as the backing store for AnimatedNormals.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty AnimatedNormalsProperty =
            DependencyProperty.Register("AnimatedNormals", typeof(Vector3DCollection), typeof(Document), new UIPropertyMetadata(null));



        public Document()
            : this(BoneAnimationLibrary.FromEmptyRoot())
        {
        }

        public Document(BoneAnimationLibrary animationLibrary)
        {
            _animationLibrary = animationLibrary;
            _interpolator = new MeshInterpolator(_animationLibrary);
            _materialLibrary = new MaterialLibrary(string.Empty);
            SelectedPointIds = new Int32Collection();
            
        }



        public Skeleton Skeleton
        {
            get { return _animationLibrary.Skeleton; }
        }
        

        public static Document Load(Stream animationLibraryStream, Stream materialLibraryStream)
        {
            Document document = new Document(BoneAnimationLibrary.Load(animationLibraryStream));
            document._materialLibrary = MaterialLibrary.Load(materialLibraryStream);
            return document;
        }

        public void Save(Stream animationLibraryStream, Stream materialLibraryStream)
        {
            this._animationLibrary.Save(animationLibraryStream);
            this._materialLibrary.Save(materialLibraryStream);
        }
        

        public void SelectBone(int id)
        {
            SelectedBoneId = id;
        }

        public void UnselectBone(int id)
        {
            SelectedBoneId = -1;
        }

        public void SetBoneSize(int boneId, double size)
        {
            if (_animationLibrary.SetBoneSize(boneId, size))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public void SetBoneName(int boneId, string name)
        {
            if (_animationLibrary.SetBoneName(boneId, name))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public void MoveBone(int boneId, Vector3D offset)
        {
            if (_animationLibrary.MoveBone(boneId, offset))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public void AddBone(int parentBoneId, string name, Point3D position, double size)
        {
            if (_animationLibrary.AddBone(parentBoneId, name, position, size))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public Int32Collection GetAttachedPoints(int boneId)
        {
            return _animationLibrary.GetAttachedPoints(boneId);
        }

        public DoubleCollection GetPointWeights(int boneId)
        {
            return _animationLibrary.GetPointWeights(boneId);
        }

        public void AddSelectedPointIds(Int32Collection selectedPointIds)
        {
            Int32Collection newCollection = new Int32Collection(SelectedPointIds);
            for (int i = 0; i < selectedPointIds.Count; i++)
            {
                if (!newCollection.Contains(selectedPointIds[i]))
                {
                    newCollection.Add(selectedPointIds[i]);
                }
            }
            SelectedPointIds = newCollection;
        }

        public void RemoveSelectedPointIds(Int32Collection selectedPointIds)
        {
            Int32Collection newCollection = new Int32Collection(SelectedPointIds);
            for (int i = 0; i < selectedPointIds.Count; i++)
            {
                if (newCollection.Contains(selectedPointIds[i]))
                {
                    newCollection.Remove(selectedPointIds[i]);
                }
            }
            SelectedPointIds = newCollection;
        }

        public void AttachPointsToBone(int boneId, Int32Collection pointIds)
        {
            if (_animationLibrary.AttachPointsToBone(boneId, pointIds))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public void DetachPointsFromBone(int boneId, Int32Collection pointIds)
        {
            if(_animationLibrary.DetachPointsFromBone(boneId, pointIds))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public void SetPointWeightsOnBone(int boneId, Int32Collection pointIds, double weight)
        {
            if(_animationLibrary.SetPointWeightsOnBone(boneId, pointIds, weight))
            {
                OnPropertyChanged("Skeleton");
            }
        }

        public Bone GetSelectedBone()
        {
            if (SelectedBoneId == -1 || _animationLibrary == null || _animationLibrary.Skeleton == null)
            {
                return null;
            }
            else
            {
                return _animationLibrary.Skeleton.GetBoneById(SelectedBoneId);
            }
        }

        public Point3D GetMeshPointById(int id)
        {
            return _animationLibrary.Mesh.Points[id];
        }

        public Point3DCollection MeshPoints
        {
            get
            {
                return _animationLibrary.Mesh.Points;
            }
        }

        public Bone GetRootBone()
        {
            return _animationLibrary.Skeleton.Root;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler p = PropertyChanged;
            if (p != null)
            {
                p(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion

        public Vector3DCollection MeshNormals
        {
            get
            {
                return _animationLibrary.Mesh.Normals;
            }
        }

        public PointCollection TextureCoordinates
        {
            get
            {
                return _animationLibrary.Mesh.TextureCoordinates;
            }
        }

        public Material Material
        {
            get
            {
                return _materialLibrary["Default Material"];
            }
        }

        public BoneAnimationDictionary Animations
        {
            get
            {
                return _animationLibrary.Animations;
            }
        }

        public void GetAnimationStill(double offset, out Point3DCollection points, out Vector3DCollection normals)
        {
            _interpolator.ShowAnimationStill(this.CurrentAnimation, offset);
            points = _interpolator.Mesh.Points;
            normals = _interpolator.Mesh.Normals;
        }
    }
}
